package com.hexiang.client.impl;

import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.Sph;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.hexiang.client.UserHttpApiClient;
import com.hexiang.client.handler.ClientResponseHanlder;
import com.hexiang.client.handler.UserHttpApiClientBlockHander;
import com.hexiang.domain.CommonResponse;
import com.hexiang.domain.User;
import com.hexiang.exception.ClientException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.*;
import reactor.core.publisher.Mono;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.time.Duration;
import java.time.temporal.ChronoUnit;

@Component
@Slf4j
public class UserHttpApiClientImpl implements UserHttpApiClient {

    private static final String USER_HTTP_URL = "http://localhost:8080";

    private WebClient webClient = WebClient.builder()
            .clientConnector(new ReactorClientHttpConnector())
            .baseUrl(USER_HTTP_URL)
            .filter(logRequest())
            .filter(logResponse())
            .defaultHeader(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)")
            .defaultCookie("ACCESS_TOKEN", "test_token").build();

    private ExchangeFilterFunction logRequest() {
        return (clientRequest, next) -> {
            log.info("Request: {} {}", clientRequest.method(), clientRequest.url());
            clientRequest.headers()
                    .forEach((name, values) -> values.forEach(value -> log.info("{}={}", name, value)));
            return next.exchange(clientRequest);
        };
    }

    private ExchangeFilterFunction logResponse() {
        return (request, next) ->
            next.exchange(request).flatMap(clientResponse -> Mono.just(clientResponse));
    }


    @SentinelResource(value = "getUsers", entryType = EntryType.IN, blockHandlerClass = UserHttpApiClientBlockHander.class, blockHandler = "getUsers")
    @Override
    public Mono<CommonResponse<Serializable>> getUsers() throws ClientException {
        Mono<CommonResponse<Serializable>> commonResponseMono = null;
        String url = "/1111users";
        try {
            commonResponseMono = webClient.get()
                    .uri(url)
                    .retrieve()
                    .onStatus(HttpStatus::isError, res -> Mono.error(new ClientException(res.statusCode().value(), res.statusCode().getReasonPhrase())))
                    .bodyToMono(String.class)
                    .timeout(Duration.of(2, ChronoUnit.SECONDS))
                    .flatMap(cr -> Mono.just(CommonResponse.builder().body(cr).succ(true).build()))
                    .doOnError(throwable -> Mono.just(CommonResponse.builder().msg("超时").succ(true).build()));
        } catch (Exception e) {
            log.error("getUserByNameErr", e);
            ClientResponseHanlder.handleExceptionThrow("根据用户名查询用户信息", e);
        }
        return commonResponseMono;
    }

    @Override
    public Mono<CommonResponse> getUserByName(String name) throws ClientException {
        Mono<CommonResponse> commonResponseMono = null;

        String url = "/user/" + name;
        try {
            commonResponseMono = webClient.get()
                    .uri(url)
                    .retrieve()
                    .onStatus(HttpStatus::isError, res -> Mono.error(new ClientException(res.statusCode().value(), res.statusCode().getReasonPhrase())))
                    .bodyToMono(String.class).timeout(Duration.of(5, ChronoUnit.SECONDS))
                    .flatMap(cr -> Mono.just(CommonResponse.builder().body(cr).succ(true).build()));

        } catch (Exception e) {
            log.error("getUserByNameErr,params is {}", name, e);
            ClientResponseHanlder.handleExceptionThrow("根据用户名查询用户信息", e);
        }

        return commonResponseMono;
    }

    @Override
    public Mono<CommonResponse> addUser(String name, int age) throws ClientException {
        Mono<CommonResponse> commonResponseMono = null;
        String url = "/user";
        try {
            // 提交参数设置
            MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
            formData.add("name", name);
            formData.add("age", String.valueOf(age));

            commonResponseMono = webClient.post().uri(url).contentType(MediaType.parseMediaType(MediaType.APPLICATION_FORM_URLENCODED_VALUE))
                    .body(BodyInserters.fromFormData(formData))
                    .retrieve()
                    .onStatus(HttpStatus::isError, res -> Mono.error(new ClientException(res.statusCode().value(), res.statusCode().getReasonPhrase())))
                    .bodyToMono(String.class)
                    .timeout(Duration.of(5, ChronoUnit.SECONDS))
                    .flatMap(cr -> Mono.just(CommonResponse.builder().body(cr).succ(true).build()));


        } catch (Exception e) {
            log.error("addUserErr,params is{}", String.format("name is %s,age is %s", name, age), e);
            ClientResponseHanlder.handleExceptionThrow("新增用户", e);
        }
        return commonResponseMono;
    }

    @Override
    public Mono<CommonResponse> deleteUserById(Long id) throws ClientException {
        Mono<CommonResponse> commonResponseMono = null;
        String url = "/user/" + id;
        try {
            commonResponseMono = webClient.delete().uri(url)
                    .retrieve()
                    .onStatus(HttpStatus::isError, res -> Mono.error(new ClientException(res.statusCode().value(), res.statusCode().getReasonPhrase())))
                    .bodyToMono(String.class)
                    .timeout(Duration.of(5, ChronoUnit.SECONDS))
                    .flatMap(cr -> Mono.just(CommonResponse.builder().body(cr).succ(true).build()));

        } catch (Exception e) {
            log.error("deleteUserByIdErr,params is {}", id, e);
            ClientResponseHanlder.handleExceptionThrow("根据id删除用户", e);
        }
        return commonResponseMono;
    }

    @Override
    public Mono<CommonResponse> updateUser(User user) throws ClientException {

        Mono<CommonResponse> commonResponseMono = null;
        String url = "/user";
        try {
            commonResponseMono = webClient.put().uri(url)
                    .bodyValue(user)
                    .retrieve()
                    .onStatus(HttpStatus::isError, res -> Mono.error(new ClientException(res.statusCode().value(), res.statusCode().getReasonPhrase())))
                    .bodyToMono(String.class)
                    .timeout(Duration.of(5, ChronoUnit.SECONDS))
                    .flatMap(cr -> Mono.just(CommonResponse.builder().body(cr).succ(true).build()));

        } catch (Exception e) {
            log.error("updateUserErr,params is {}", user, e);
            ClientResponseHanlder.handleExceptionThrow("修改用户", e);
        }
        return commonResponseMono;
    }

    @Override
    public Mono<CommonResponse> getUserPage(Integer pageNum, Integer pageSize) throws ClientException {

        Mono<CommonResponse> commonResponseMono = null;
        String url = "/user/page?pageNum={pageNum}&pageSize={pageSize}";
        try {

            commonResponseMono = webClient.get().uri(url, pageNum, pageSize)
                    .retrieve()
                    .onStatus(HttpStatus::isError, res -> Mono.error(new ClientException(res.statusCode().value(), res.statusCode().getReasonPhrase())))
                    .bodyToMono(String.class)
                    .timeout(Duration.of(5, ChronoUnit.SECONDS))
                    .flatMap(cr -> Mono.just(CommonResponse.builder().body(cr).succ(true).build()));


        } catch (Exception e) {
            log.error("getUserPageErr,params is {}", String.format("pageNum is %s,pageSize is %s", pageNum, pageSize), e);
            ClientResponseHanlder.handleExceptionThrow("分页查询用户信息", e);
        }
        return commonResponseMono;
    }

    @SentinelResource(value = "errTest", entryType = EntryType.IN, blockHandlerClass = UserHttpApiClientBlockHander.class, blockHandler = "errTest")
    @Override
    public Mono<CommonResponse> errTest() throws ClientException {
        Mono<CommonResponse> commonResponseMono = null;

        try {
            String url = "/user/err";
            commonResponseMono = webClient.get().uri(url)
                    .retrieve()
                    .onStatus(HttpStatus::isError, res -> Mono.error(new ClientException(res.statusCode().value(), res.statusCode().getReasonPhrase())))
                    .bodyToMono(String.class)
                    .timeout(Duration.of(5, ChronoUnit.SECONDS))
                    .flatMap(cr -> Mono.just(CommonResponse.builder().body(cr).succ(true).build()));

        } catch (Exception e) {
            log.error("errTestErr", e);
            ClientResponseHanlder.handleExceptionThrow("异常测试", e);
        }
        return commonResponseMono;
    }
}